/**
 * Copyright (c) 2016 Razeware LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


import UIKit

class WhateverViewController: UIViewController {
  // 1、标识后台任务
  var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
  var previous = NSDecimalNumber.one
  var current = NSDecimalNumber.one
  var position: UInt = 1
  var updateTimer: Timer?
  
  
  @IBOutlet var resultsLabel: UILabel!
  
  // 2、告诉iOS系统，一旦app进入后台，app需要有更多的时间完成它正在做的任何事情。直到你调用`endBackgroundTask()`后台才会停止获取CPU时间。
  func registerBackgroundTask()  {
    backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
      // 7、通常情况下，后台运行时间会从180秒开始，一直减少到5秒。
      // 当时间减少到大约5秒的时候，该expiration block会被调用。
      // 如果你在这里关掉后台运行，后台运行也就随之结束了。当你app重新活跃起来，必须重新注册后台处理，否则之后在退回后台，便没有后台处理。
      // 如果你在这expiration block中并没有关掉后台task，他将会一直进行下去。
      self?.endBackgroundTask()
    }
    assert(backgroundTask != UIBackgroundTaskInvalid)
  }
  
  // 3、如果你没有在一段时间后调用 endBackgroundTask()，ios会在剩余5秒左右调用expiration block。所以，最好在完成任务后，主动去调用 endBackgroundTask()。如果你没有在expiration block里面调用 endBackgroundTask()，而是一直在后台执行，你的app将会被终止。
  func endBackgroundTask()  {
    print("Background task ended.")
    UIApplication.shared.endBackgroundTask(backgroundTask)
    backgroundTask = UIBackgroundTaskInvalid
  }
  
  // 调用beginBackgroundTask(expirationHandler:)和调用 endBackgroundTask()应该要配套。如果你调用了beginBackgroundTaskWithExpirationHandler(_:)两次，只调用了 endBackgroundTask()一次，app将会一直向后台索要cpu time，直到你再次调用 endBackgroundTask()
  @IBAction func didTapPlayPause(_ sender: UIButton) {
    sender.isSelected = !sender.isSelected
    if sender.isSelected {
      resetCalculation()
      updateTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self,
                                         selector: #selector(calculateNextNumber), userInfo: nil, repeats: true)
      // 4、注册后台处理，当app退到后台，进程还会继续计算斐波拉契数列
      // register background task
      self.registerBackgroundTask()
    } else {
      updateTimer?.invalidate()
      updateTimer = nil
      // 5、用户主动关掉计算，所以当app处于后台，也不需要做计算。
      // end background task
      if backgroundTask != UIBackgroundTaskInvalid {
        endBackgroundTask()
      }
    }
  }
  
  func calculateNextNumber() {
    let result = current.adding(previous)
    
    let bigNumber = NSDecimalNumber(mantissa: 1, exponent: 40, isNegative: false)
    if result.compare(bigNumber) == .orderedAscending {
      previous = current
      current = result
      position += 1
    } else {
      // This is just too much.... Start over.
      resetCalculation()
    }
    
    let resultsMessage = "Position \(position) = \(current)"
    
    // 6、只有app处于活跃状态才会更新label。当应用退到后台，计算结果会在控制台输出，并且输出剩余的后台时间
    switch UIApplication.shared.applicationState {
    case .active:
      resultsLabel.text = resultsMessage
    case .background:
      print("App is backgrounded. Next number = \(resultsMessage)")
      print("Background time remaining = \(UIApplication.shared.backgroundTimeRemaining) seconds")
    case .inactive:
      break
    }
    
  }
 
  func resetCalculation() {
    previous = NSDecimalNumber.one
    current = NSDecimalNumber.one
    position = 1
  }
  
  // 当后台处理调用了expiration block时，重新激活app之后，需要再次注册后台task
  func reinstateBackgroundTask()  {
    if updateTimer != nil && (backgroundTask == UIBackgroundTaskInvalid) {
      registerBackgroundTask()
    }
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // 当前控制器订阅app激活的通知，当app被激活的时候，要检查后台处理是否被关掉。
    NotificationCenter.default.addObserver(self, selector: #selector(reinstateBackgroundTask), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
  }
  
  deinit {
    NotificationCenter.default.removeObserver(self)
  }

}
